//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//============================================================================

#pragma once 
#include "mesh/TopoTables.h"
#include "base/WrapperVtkmCont.h"
#include "base/WrapperVtkmTypes.h"
#include "base/WrapperVtkmWorklet.h"

struct ExtractInternalFace
{
    class NumFacesPerCell : public WorkletVisitCellsWithPoints
    {
    public:
        using ControlSignature = void(CellSetIn inCellSet, FieldOut numFacesInCell);
        using ExecutionSignature = void(CellShape, _2);
        using InputDomain = _1;

        template <typename CellShapeTag>
        VTKM_EXEC void operator()(CellShapeTag shape, IdComponent& numFaces) const
        {
            CellFaceNumberOfFaces(shape, numFaces);
        }
    };

    struct BuildDuplicateFace : public WorkletVisitCellsWithPoints
    {
        using ControlSignature = void(CellSetIn cellset, FieldOut face_canonical_id, FieldOut face_duplicate, FieldOut face_shape, FieldOut face_num_pts);
        using ExecutionSignature = void( CellShape, PointIndices, InputIndex, VisitIndex, _2, _3, _4, _5);
        using InputDomain = _1;

        using ScatterType = ScatterCounting;

        template <typename CellShapeTag, typename C2NType, typename FacePairType>
        VTKM_EXEC void operator()(
            CellShapeTag cell_shape,
            const C2NType& c2n,
            Id inputIndex,
            IdComponent visitIndex,
            Id3& faceId,
            FacePairType& face,
			UInt8& face_shape,
			IdComponent& face_num_pts
			) const
        {
            CellFaceCanonicalId(visitIndex, cell_shape, c2n, faceId);
			CellFaceShape(visitIndex, cell_shape, face_shape);
			CellFaceNumberOfPoints(visitIndex, cell_shape, face_num_pts);
            face = { inputIndex, visitIndex };
        }
    };

    struct BuildUniqueFace : public WorkletReduceByKey
    {
        using ControlSignature = void(KeysIn faceKey, 
			ValuesIn faceDuplicate, ReducedValuesOut f2cL, ReducedValuesOut f2cR,
			ValuesIn faceShapeDuplicate, ReducedValuesOut faceShapeUnique,
			ValuesIn faceNumPtsDuplicate, ReducedValuesOut faceNumPtsUnique
			);
        using ExecutionSignature = void(_2, _3, _4,_5,_6,_7,_8);

        template<typename FacePairType, typename F2CType, typename FaceShapeType, typename FaceNumPtsType>
        VTKM_EXEC
        void operator()(
			const FacePairType& face_duplicate, F2CType& f2cL, F2CType& f2cR, 
			const FaceShapeType& face_shape_dup, UInt8& face_shape_unique,
			const FaceNumPtsType& face_num_pts_dup, IdComponent& face_num_pts_unique
			) const
        {
			face_shape_unique = face_shape_dup[0];
			face_num_pts_unique = face_num_pts_dup[0];

            if (face_duplicate.GetNumberOfComponents() == 2)
            {
                f2cL = face_duplicate[0];
                f2cR = face_duplicate[1];
            }
            else if(face_duplicate.GetNumberOfComponents() == 1)
            {
                f2cL = face_duplicate[0];
                f2cR = {-1, -1};
            }
            else
            {
                this->RaiseError("face_duplicate.GetNumberOfComponents error.");
            }
        }
    };

	struct BuildFaceConnectivity : public WorkletMapField
	{
		using ControlSignature = void(FieldIn f2cL, WholeCellSetIn<> cellSet, FieldOut f2n);
		using ExecutionSignature = void(_1, _2, VisitIndex, _3);
		using InputDomain = _1;
		using ScatterType = ScatterCounting;

		template <typename F2CType, typename CellSetType, typename F2NType>
		VTKM_EXEC void operator()(const F2CType& f2cL, const CellSetType& cell_set, IdComponent visitIndex, F2NType& f2n) const
		{
			auto cell_index = f2cL.first;
			auto face_index = f2cL.second;
			auto cell_shape = cell_set.GetCellShape(cell_index);

			auto c2n = cell_set.GetIndices(cell_index);
			IdComponent result;
			CellFaceLocalIndex(visitIndex, face_index, cell_shape, result);
			f2n = c2n[result];
		}
	};

	struct ExtractF2C : public WorkletMapField
	{
		using ControlSignature = void(FieldIn f2c_pair, FieldOut f2c);
		using ExecutionSignature = void(_1, _2);

		template <typename F2CPairType, typename F2CType>
		VTKM_EXEC void operator()(const F2CPairType& f2c_pair, F2CType& f2c) const
		{
			f2c = f2c_pair.first;
		}
	};

	VTKM_CONT
	static void Run(const CellSetExplicit<>& in, CellSetExplicit<>& out, ArrayHandle<Id>& f2cL, ArrayHandle<Id>& f2cR)
	{
		ArrayHandle<IdComponent> num_face_per_cell;
		Invoker{}(NumFacesPerCell{}, in, num_face_per_cell);

		ArrayHandle<Id3> faceId;                    
		ArrayHandle<Pair<Id, IdComponent>> face_dup; 
		ArrayHandle<UInt8> face_shape_dup;
		ArrayHandle<IdComponent> face_num_pts_dup;
		Invoker{}(BuildDuplicateFace{}, ScatterCounting{num_face_per_cell}, in, faceId, face_dup, face_shape_dup, face_num_pts_dup);
		Keys<Id3> faceKeys(faceId);

		ArrayHandle<Pair<Id, IdComponent>> f2cL_pair, f2cR_pair;
		ArrayHandle<UInt8> face_shape;
		ArrayHandle<IdComponent> num_point_per_face;
		Invoker{}(BuildUniqueFace{}, faceKeys, face_dup, f2cL_pair, f2cR_pair, face_shape_dup, face_shape, face_num_pts_dup, num_point_per_face);

		ArrayHandle<Id> off_set;
		off_set = ConvertNumIndicesToOffsets(num_point_per_face);
		ArrayHandle<Id> f2n;

		Invoker{}(BuildFaceConnectivity{}, ScatterCounting{num_point_per_face}, f2cL_pair, in, f2n);
		
		
		out.Fill(in.GetNumberOfPoints(), face_shape, f2n, off_set);

		Invoker{}(ExtractF2C{}, f2cL_pair, f2cL);
		Invoker{}(ExtractF2C{}, f2cR_pair, f2cR);

	}
};